﻿/**
 *
 * power_manager.c
 *
 *  Created on: 2019-10-25
 *      Author: chenshisheng
 */

#include "power_manager.h"
#include "ctimer.h"
#include "rtc.h"
#include "hw.h"
#include "active.h"
#include "watch_dog.h"
#include "message_queue.h"

// 例行检测时间间隔（秒）
#define ROUTINE_DETECT_INTERVAL (30 * 60)

#define HEARTBEAT_INTERVAL (30 * 60)

// 系统自动重启时间间隔（秒）
#define RESET_INTERVAL (60 * 60 * 24)

// 定时器定时中断时间间隔（秒）
// 需要在定时中断内清零看门狗
#define TIM_UPDATE_INTERVAL RTC_WAKEUP_INTERVAL

#if TIM_UPDATE_INTERVAL > WATCH_DOG_RELOAD_TIMING
#   error "TIM_UPDATE_INTERVAL to long!"
#endif

static struct
{
    unsigned int routineCounter;
    unsigned int heartbeatCounter;
    unsigned int heartbeat;
    unsigned int resetCounter;
    bool isInLowPowerMode;
}_ctrl;

static Ctimer_t _runTimer;
static Ctimer_t _awakeTimer;

extern void SystemClock_Config(void);
static void _Test(int data);

void PowerManager_Init(void)
{
    _ctrl.routineCounter = 0;
    _ctrl.heartbeatCounter = 0;
    _ctrl.heartbeat = 0;
    _ctrl.resetCounter = 0;
    _ctrl.isInLowPowerMode = false;

    /* Enable Power Control clock */
    __HAL_RCC_PWR_CLK_ENABLE();

    HW_SetExtiCallback(HW_Exti_User, PowerManager_ActiveByUser, HW_Exti_User);
    PowerManager_ActiveByUser(HW_Exti_User);

	MsgQueue_Send(MsgQueue_Id_Heartbeat, &_ctrl.heartbeat, sizeof(_ctrl.heartbeat));
	_ctrl.heartbeat ++;

	MX_RTC_Init();
    WatchDog_Init();
}

void PowerManager_Sleep(void)
{
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}

void PowerManager_TryToStop(void)
{
    if(Active_HaveAction())
    {
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
        return;
    }

    ON_DEBUG(HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI));
    ON_RELEASE(_ctrl.isInLowPowerMode = true);
    ON_RELEASE(HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI));
}

void PowerManager_ExitLowPowerMode(void)
{
    if(! _ctrl.isInLowPowerMode)
    {
        return;
    }

    /* Configures system clock after wake-up from STOP */
    SystemClock_Config();
    _ctrl.isInLowPowerMode = false;
}

static inline void _WakeupCallback(void)
{
    WatchDog_Reload();
    _ctrl.routineCounter += TIM_UPDATE_INTERVAL;
    if(_ctrl.routineCounter >= ROUTINE_DETECT_INTERVAL)
    {
        _ctrl.routineCounter = 0;
        PowerManager_ExitLowPowerMode();
        MsgQueue_Send(MsgQueue_Id_RoutineDetect, NULL, 0);
    }

    _ctrl.heartbeatCounter += TIM_UPDATE_INTERVAL;
    if(_ctrl.heartbeatCounter >= HEARTBEAT_INTERVAL)
    {
        _ctrl.heartbeatCounter = 0;
        MsgQueue_Send(MsgQueue_Id_Heartbeat, &_ctrl.heartbeat, sizeof(_ctrl.heartbeat));
        _ctrl.heartbeat ++;
    }

    _ctrl.resetCounter += TIM_UPDATE_INTERVAL;
    if(_ctrl.resetCounter >= RESET_INTERVAL)
    {
        HAL_NVIC_SystemReset();
    }
}

#if 0
/**
  * @brief  Autoreload match callback in non blocking mode
  * @param  hlptim : LPTIM handle
  * @retval None
  */
void HAL_LPTIM_AutoReloadMatchCallback(LPTIM_HandleTypeDef *hlptim)
{
    _WakeupCallback();
}
#else
/**
  * @brief  Wake Up Timer callback.
  * @param  hrtc RTC handle
  * @retval None
  */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hrtc);
  _WakeupCallback();

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_RTCEx_WakeUpTimerEventCallback could be implemented in the user file
   */
}
#endif

static void _OnTimeout(int data)
{
    UNUSED(data);

    HW_Config(HW_Out_LoadEn, HW_State_Inactive);
    Active_Set(Active_User, false);
    Ctimer_Stop(&_runTimer);
    HW_Config(HW_Out_Run, HW_State_Inactive);
}

static void _Test(int data)
{
    UNUSED(data);
    HW_Toggle(HW_Out_Run);
}

void PowerManager_ActiveByUser(int data)
{
    UNUSED(data);

    PowerManager_ExitLowPowerMode();
    Active_Set(Active_User, true);
    HW_Config(HW_Out_LoadEn, HW_State_Active);
    Ctimer_Start(&_awakeTimer, 3 * CTIMER_TICK_MINUTE, _OnTimeout, CTIMER_FLAG_ONCE, 0);
    Ctimer_Start(& _runTimer, 500, _Test, 0, 0);
}
